package jsyntaxpane.util.intellisense;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;

/**
 * Utilitários para I/O
 * 
 */
public final class IOUtils {

    public static final int BUFFER_SIZE = 1024;

    /**
     * 
     */
    private IOUtils() {
    }

    /**
     * Lê de um stream inteiro, devolvendo o conteudo e fechando a stream.
     * 
     * @param input {@link InputStream}
     * @throws IOException IOException
     * @return byte[]
     */
    public static byte[] readFully(final InputStream input) throws IOException {
        ByteArrayOutputStream ret = new ByteArrayOutputStream();
        copyFullyClose(input, ret);
        return ret.toByteArray();
    }

    /**
     * Copy the content from the InputStream into a temporary file that will be deleted when this JVM exits normally
     * 
     * @param pref the preffix
     * @param suffix the suffix
     * @param source the source {@link InputStream}
     * @return {@link File}
     * @throws IOException if an IOException occurs
     */
    public static File copyToTempFile(final String pref, final String suffix, final InputStream source) throws IOException {
        return copyToTempFile(pref, suffix, null, source);
    }

    /**
     * Copy the content from the InputStream into a temporary file that will be deleted when this JVM exits normally
     * 
     * @param pref the preffix
     * @param suffix the suffix
     * @param parent the parent folder
     * @param source the inputStream
     * @return File
     * @throws IOException if an iooexception occurs
     */
    public static File copyToTempFile(final String pref, final String suffix, final File parent, final InputStream source)
            throws IOException {
        File file = File.createTempFile(pref, suffix, parent);
        IOUtils.copyFullyClose(source, new FileOutputStream(file));
        file.deleteOnExit();
        return file;

    }

    /**
     * Copy the bytes fully from the source into the output, closing both streams in the end.
     * 
     * @param source the source input stream
     * @param output the output
     * @throws IOException if an IOException occurs, either reading, writing or closing any of the streams.
     */
    public static void copyFullyClose(final InputStream source, final OutputStream output) throws IOException {
        try {
            byte[] buffer = new byte[BUFFER_SIZE];
            int read = -1;
            while ((read = source.read(buffer)) >= 0) {
                output.write(buffer, 0, read);
            }
        } finally {
            try {
                source.close();
            } finally {
                output.close();
            }
        }
    }

    /**
     * Serializa um {@link Object} em um array de bytes
     * 
     * @param serializable o objeto a ser serializado
     * @return byte[]
     */
    public static byte[] serializeObject(final Object serializable) {
        try {
            final ByteArrayOutputStream bout = new ByteArrayOutputStream();
            ObjectOutputStream out = new ObjectOutputStream(bout);
            out.writeObject(serializable);
            out.flush();
            out.close();
            return bout.toByteArray();
        } catch (IOException e) {
            throw new IllegalStateException("unexpected IOException", e);
        }
    }

    /**
     * Le um objeto serializado como um array de bytes
     * 
     * @param array array de bytes
     * @return Object
     * @throws ClassNotFoundException se a classe nao for encontrada no classloader
     */
    public static Object deserializeObject(final byte[] array) throws ClassNotFoundException {
        try {
            ObjectInputStream oin = new ObjectInputStream(new ByteArrayInputStream(array));
            return oin.readObject();
        } catch (IOException e) {
            throw new IllegalStateException("unexpected IOException", e);
        }
    }

    /**
     * Clona um objeto, serializando. Devolve <code>null</code> se <code>null</code> for passado
     * 
     * @param serializable o objeto a ser clonado
     * @return objeto clonado.
     */
    public static Object deepClone(final Object serializable) {
        if (serializable == null) {
            return null;
        }
        try {
            return deserializeObject(serializeObject(serializable));
        } catch (ClassNotFoundException e) {
            throw new IllegalStateException("Excecao inesperada!!", e);
        }
    }

}
